package top.hkyzf.study.j2se.juc;

import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 验证volatile的可见性
 * @author 朱峰
 * @date 2020-12-23 16:23
 */
public class VolatileDemo {

    // 1.测试 volatile 保证内存可见性的案例
    @Test
    public void testVolatile1 () {
        MyData data = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "开始执行！");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.addTo60();
            System.out.println(Thread.currentThread().getName() + "将number改为了60！");
        }, "AAA").start();

        while (true) {
            if(data.number == 60) {
                break;
            }
        }
        System.out.println("程序退出了！");
    }

    // 2.测试 volatile 不能保证原子性的案例
    @Test
    public void testVolatile2 () {
        MyData data = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    data.addAdd();
                }
            }, "AAA").start();
        }
        while (true) {
            // 默认有两个活跃线程，一个是main线程，一个是gc线程
            if (Thread.activeCount() > 2) {
                Thread.yield();
            } else {
                break;
            }
        }
        System.out.println("number的值为：" + data.number);
    }

    // 3.1 添加 synchronized 解决 volatile 不保证原子性问题
    // 3.2 使用JUC下的AtomicInteger解决
    @Test
    public void testVolatile3 () {
        MyData data = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    data.addAddNum();
                    data.addAyomic();
                }
            }, "AAA").start();
        }
        while (true) {
            // 默认有两个活跃线程，一个是main线程，一个是gc线程
            if (Thread.activeCount() > 2) {
                Thread.yield();
            } else {
                break;
            }
        }
        System.out.println("num的值为：" + data.num);
        System.out.println("atomicInteger的值为：" + data.atomicInteger);
    }
}

/**
 * 资源类
 */
class MyData {
    volatile int number = 0;
    int num = 0;
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addTo60() {
        this.number = 60;
    }
    public void addAdd() {
        this.number++;
    }
    public synchronized void addAddNum() {
        this.num++;
    }

    public void addAyomic() {
        atomicInteger.getAndIncrement();
    }
}

/**
 * 指令重排案例（多线程有可能出问题）
 * 多线程环境中线程交替执行，由于编译器优化重排的存在，两个线程中使用的变量能否保证一致性是无法确定的，结果无法预测
 */
class ReSort {
    private int a = 0;
    private boolean flag = false;

    // 假如这里出现了指令重排，会导致下面方法输出不确定
    public void method1() {
        a = 1;
        flag = true;
    }

    public void method2() {
        if(flag) {
            a = a + 5;
            System.out.println("a的值为：" + a);
        }
    }
}
